/*-------------------------------------------------------------------------
 *
 * ginarrayproc.c
 *	  GIN 的任何数组索引的支持函数
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *	  src/backend/access/gin/ginarrayproc.c
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/gin.h"
#include "access/stratnum.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"


#define GinOverlapStrategy		1
#define GinContainsStrategy		2
#define GinContainedStrategy	3
#define GinEqualStrategy		4


/*
 * extractValue 支持函数
 */
Datum ginarrayextract(PG_FUNCTION_ARGS)
{
	/* 复制数组输入以确保它在使用过程中不会消失 */
	ArrayType  *fc_array = PG_GETARG_ARRAYTYPE_P_COPY(0);
	int32	   *fc_nkeys = (int32 *) PG_GETARG_POINTER(1);
	bool	  **fc_nullFlags = (bool **) PG_GETARG_POINTER(2);
	int16		fc_elmlen;
	bool		fc_elmbyval;
	char		fc_elmalign;
	Datum	   *fc_elems;
	bool	   *fc_nulls;
	int			fc_nelems;

	get_typlenbyvalalign(ARR_ELEMTYPE(fc_array),
						 &fc_elmlen, &fc_elmbyval, &fc_elmalign);

	deconstruct_array(fc_array,
					  ARR_ELEMTYPE(fc_array),
					  fc_elmlen, fc_elmbyval, fc_elmalign,
					  &fc_elems, &fc_nulls, &fc_nelems);

	*fc_nkeys = fc_nelems;
	*fc_nullFlags = fc_nulls;

	/* 我们不应释放数组， elems[i] 指向其中 */
	PG_RETURN_POINTER(fc_elems);
}

/*
 * 以前， ginarrayextract 只有两个参数。现在有三个，
 * 但我们仍需要一个带有两个参数的 pg_proc 条目以支持重新加载
 * 9.1 之前的 contrib/intarray 操作类声明。这个兼容性
 * 函数最终应该消失。
 */
Datum ginarrayextract_2args(PG_FUNCTION_ARGS)
{
	if (PG_NARGS() < 3)			/* 不应该发生 */
		elog(ERROR, "ginarrayextract requires three arguments");
	return ginarrayextract(fcinfo);
}

/*
 * extractQuery 支持函数
 */
Datum ginqueryarrayextract(PG_FUNCTION_ARGS)
{
	/* 复制数组输入以确保它在使用过程中不会消失 */
	ArrayType  *fc_array = PG_GETARG_ARRAYTYPE_P_COPY(0);
	int32	   *fc_nkeys = (int32 *) PG_GETARG_POINTER(1);
	StrategyNumber fc_strategy = PG_GETARG_UINT16(2);

	/* bool   **pmatch = (bool **) PG_GETARG_POINTER(3); */
	/* Pointer	   *extra_data = (Pointer *) PG_GETARG_POINTER(4); */
	bool	  **fc_nullFlags = (bool **) PG_GETARG_POINTER(5);
	int32	   *fc_searchMode = (int32 *) PG_GETARG_POINTER(6);
	int16		fc_elmlen;
	bool		fc_elmbyval;
	char		fc_elmalign;
	Datum	   *fc_elems;
	bool	   *fc_nulls;
	int			fc_nelems;

	get_typlenbyvalalign(ARR_ELEMTYPE(fc_array),
						 &fc_elmlen, &fc_elmbyval, &fc_elmalign);

	deconstruct_array(fc_array,
					  ARR_ELEMTYPE(fc_array),
					  fc_elmlen, fc_elmbyval, fc_elmalign,
					  &fc_elems, &fc_nulls, &fc_nelems);

	*fc_nkeys = fc_nelems;
	*fc_nullFlags = fc_nulls;

	switch (fc_strategy)
	{
		case GinOverlapStrategy:
			*fc_searchMode = GIN_SEARCH_MODE_DEFAULT;
			break;
		case GinContainsStrategy:
			if (fc_nelems > 0)
				*fc_searchMode = GIN_SEARCH_MODE_DEFAULT;
			else				/* 一切都包含空集合 */
				*fc_searchMode = GIN_SEARCH_MODE_ALL;
			break;
		case GinContainedStrategy:
			/* 空集合包含在一切中 */
			*fc_searchMode = GIN_SEARCH_MODE_INCLUDE_EMPTY;
			break;
		case GinEqualStrategy:
			if (fc_nelems > 0)
				*fc_searchMode = GIN_SEARCH_MODE_DEFAULT;
			else
				*fc_searchMode = GIN_SEARCH_MODE_INCLUDE_EMPTY;
			break;
		default:
			elog(ERROR, "ginqueryarrayextract: unknown strategy number: %d",
				 fc_strategy);
	}

	/* 我们不应释放数组， elems[i] 指向其中 */
	PG_RETURN_POINTER(fc_elems);
}

/*
 * 一致性支持函数
 */
Datum ginarrayconsistent(PG_FUNCTION_ARGS)
{
	bool	   *fc_check = (bool *) PG_GETARG_POINTER(0);
	StrategyNumber fc_strategy = PG_GETARG_UINT16(1);

	/* ArrayType  *query = PG_GETARG_ARRAYTYPE_P(2); */
	int32		fc_nkeys = PG_GETARG_INT32(3);

	/* Pointer	   *extra_data = (Pointer *) PG_GETARG_POINTER(4); */
	bool	   *fc_recheck = (bool *) PG_GETARG_POINTER(5);

	/* Datum	   *queryKeys = (Datum *) PG_GETARG_POINTER(6); */
	bool	   *fc_nullFlags = (bool *) PG_GETARG_POINTER(7);
	bool		fc_res;
	int32		fc_i;

	switch (fc_strategy)
	{
		case GinOverlapStrategy:
			/* 结果不是损失性 */
			*fc_recheck = false;
			/* 至少必须有一个非空元素的匹配 */
			fc_res = false;
			for (fc_i = 0; fc_i < fc_nkeys; fc_i++)
			{
				if (fc_check[fc_i] && !fc_nullFlags[fc_i])
				{
					fc_res = true;
					break;
				}
			}
			break;
		case GinContainsStrategy:
			/* 结果不是损失性 */
			*fc_recheck = false;
			/* 所有元素在 check[] 中必须为真，并且没有空值 */
			fc_res = true;
			for (fc_i = 0; fc_i < fc_nkeys; fc_i++)
			{
				if (!fc_check[fc_i] || fc_nullFlags[fc_i])
				{
					fc_res = false;
					break;
				}
			}
			break;
		case GinContainedStrategy:
			/* 我们将需要重新检查 */
			*fc_recheck = true;
			/* 这里无法做其他有用的事情 */
			fc_res = true;
			break;
		case GinEqualStrategy:
			/* 我们将需要重新检查 */
			*fc_recheck = true;

			/*
			 * 所有元素在 check[] 中必须为真；这里没有对空值的歧视
			 * 这是因为 array_contain_compare 和
			 * array_eq 不同地处理空值 ...
			 */
			fc_res = true;
			for (fc_i = 0; fc_i < fc_nkeys; fc_i++)
			{
				if (!fc_check[fc_i])
				{
					fc_res = false;
					break;
				}
			}
			break;
		default:
			elog(ERROR, "ginarrayconsistent: unknown strategy number: %d",
				 fc_strategy);
			fc_res = false;
	}

	PG_RETURN_BOOL(fc_res);
}

/*
 * 三一致性支持函数
 */
Datum ginarraytriconsistent(PG_FUNCTION_ARGS)
{
	GinTernaryValue *fc_check = (GinTernaryValue *) PG_GETARG_POINTER(0);
	StrategyNumber fc_strategy = PG_GETARG_UINT16(1);

	/* ArrayType  *query = PG_GETARG_ARRAYTYPE_P(2); */
	int32		fc_nkeys = PG_GETARG_INT32(3);

	/* Pointer	   *extra_data = (Pointer *) PG_GETARG_POINTER(4); */
	/* Datum	   *queryKeys = (Datum *) PG_GETARG_POINTER(5); */
	bool	   *fc_nullFlags = (bool *) PG_GETARG_POINTER(6);
	GinTernaryValue fc_res;
	int32		fc_i;

	switch (fc_strategy)
	{
		case GinOverlapStrategy:
			/* 至少必须有一个非空元素的匹配 */
			fc_res = GIN_FALSE;
			for (fc_i = 0; fc_i < fc_nkeys; fc_i++)
			{
				if (!fc_nullFlags[fc_i])
				{
					if (fc_check[fc_i] == GIN_TRUE)
					{
						fc_res = GIN_TRUE;
						break;
					}
					else if (fc_check[fc_i] == GIN_MAYBE && fc_res == GIN_FALSE)
					{
						fc_res = GIN_MAYBE;
					}
				}
			}
			break;
		case GinContainsStrategy:
			/* 所有元素在 check[] 中必须为真，并且没有空值 */
			fc_res = GIN_TRUE;
			for (fc_i = 0; fc_i < fc_nkeys; fc_i++)
			{
				if (fc_check[fc_i] == GIN_FALSE || fc_nullFlags[fc_i])
				{
					fc_res = GIN_FALSE;
					break;
				}
				if (fc_check[fc_i] == GIN_MAYBE)
				{
					fc_res = GIN_MAYBE;
				}
			}
			break;
		case GinContainedStrategy:
			/* 这里无法做其他有用的事情 */
			fc_res = GIN_MAYBE;
			break;
		case GinEqualStrategy:

			/*
			 * 所有元素在 check[] 中必须为真；这里没有对空值的歧视
			 * 这是因为 array_contain_compare 和
			 * array_eq 不同地处理空值 ...
			 */
			fc_res = GIN_MAYBE;
			for (fc_i = 0; fc_i < fc_nkeys; fc_i++)
			{
				if (fc_check[fc_i] == GIN_FALSE)
				{
					fc_res = GIN_FALSE;
					break;
				}
			}
			break;
		default:
			elog(ERROR, "ginarrayconsistent: unknown strategy number: %d",
				 fc_strategy);
			fc_res = false;
	}

	PG_RETURN_GIN_TERNARY_VALUE(fc_res);
}
